package com.voltmao.webpro.utils.wechat;


import com.voltmao.webpro.utils.*;
import com.voltmao.webpro.utils.http.HttpClientUtil;
import com.voltmao.webpro.utils.http.HttpRequestUtil;
import com.voltmao.webpro.utils.http.HttpResponseUtils;
import com.voltmao.webpro.utils.wechat.app.CommandController;
import com.voltmao.webpro.utils.wechat.core.*;
import com.voltmao.webpro.utils.wechat.utils.MenuUtils;
import com.voltmao.webpro.utils.wechat.utils.MsgUtils;
import com.voltmao.webpro.utils.wechat.utils.PayUtils;
import com.voltmao.webpro.utils.wechat.utils.QRCodeUtils;
import com.voltmao.webpro.utils.wechat.utils.dto.WechatUserInfo;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;

/**
 * Created by zhouyang on 2017/1/5.
 * <p>
 * 微信应用的操作类
 * 支付
 * 消息
 * 菜单
 * <p>
 * 依赖文件:
 * wx-config.properties
 */
public class WechatUtils implements IWechatUtils {

    private static Logger logger = Logger.getLogger(WechatUtils.class.getName());

    private String appName;
    private final static Map<String, String> wxConfig = PropertiesFileUtils.getProperties("wx-config.properties");
    private static Map<String, WechatUtils> container = new HashMap();                   //对象容器
    private Account account;                                                            //账户信息
    private Map<String, Object> configMap;
    private AccessToken accessToken;                                                    //AccessToken 调用凭证
    private JsApiTicket jsApiTicket;                                                    //JsApiTicket 调用凭证
    private MsgUtils msgUtils;                                                          //消息工具对象
    private PayUtils payUtils;                                                          //支付工具对象
    private MenuUtils menuUtils;                                                        //菜单工具对象
    private QRCodeUtils qrCodeUtils;

    /**
     * 根据配置文件_appName 获取微信操作类   (wx-xxxxx.properties)
     * appName为xxxxxx
     *
     * @param appName
     */
    private WechatUtils(String appName) {

        this.appName = appName;

        this.configMap = PropertiesFileUtils.getProperties("wx-" + appName + ".properties");

        //根据配置文件加载账户信息
        this.account = (Account) PropertiesUtils.mapToObject(configMap, Account.class);

        //授权认证类
        this.accessToken = new AccessToken(this.account, this.getWxConfig());

        //消息工具类
        this.msgUtils = new MsgUtils(this);

        //支付工具类
        this.payUtils = new PayUtils(this);

        this.menuUtils = new MenuUtils(this);

        this.qrCodeUtils = new QRCodeUtils(this);
    }

    /**
     * 根据配置文件返回微信操作类的实例
     *
     * @param appName
     * @return
     */
    public synchronized static WechatUtils getInstance(String appName) {
        if (container.get(appName) == null) {
            WechatUtils example = new WechatUtils(appName);

            container.put(appName, example);
        }
        //logger.info("=====> WechatUtils.getInstance    :"+JsonUtil.ObjectToJSON(container));

        return container.get(appName);
    }

    /**
     * 微信开发者授权验证
     *
     * @param request
     * @param response
     */
    public void validDeveloperWechat(HttpServletRequest request, HttpServletResponse response) {
        logger.info("=====>  处理微信验证");
        PrintWriter writer = null;
        try {
            writer = response.getWriter();
            Map map = HttpRequestUtil.getParameterMap(request);
            logger.info("   data:" + JsonUtil.ObjectToJSON(map));
            if (wxValid(map)) {
                String echostr = map.get("echostr") + "";
                writer.write(echostr);
            } else {
                writer.write("");
            }
            writer.flush();
        } catch (IOException e) {
            logger.info(" err:" + e.getLocalizedMessage());
            writer.write("error");
        } finally {
            if (null != writer) {
                writer.close();
            }
        }

        logger.info("<=====  end 处理微信验证");
    }

    /**
     * 得到微信应用的路径地址
     * (自动:相对=>绝对)
     */
    public String getWechatUrl(String url) {

        String[] str = url.split("\\?");
        String hederUrl = "";

        //是否是绝对路径
        if (url.indexOf("http:") == -1) {
            hederUrl = account.getDeployDomain();
        }

        //是否带参数
        if (str.length == 2) {
            return hederUrl + str[0] + "?" + str[1];
        }

        return hederUrl + url;
    }

    /**
     * 得到处理WX认证授权的URL地址
     *
     * @param gotoUrl 跳转URL
     * @return
     */
    public String builderAuthUrl(String gotoUrl) {
//        处理流程:
//          1.调用微信接口的认证                            (调用 builderAuthUrl)
//          2.成功后,回调到统一的处理控制器
//          3.控制器,封装参数.
//              需用通过code进行加密认证.获取得到openid       (调用 getWechatAuthUrl)
//              进行统一的跳转.

        String authorizeUrl = wxConfig.get("wx.oauth2.authcode")
                .replace("APPID", account.getAppId())
                .replace("REDIRECT_URI", StringUtils.getUrlEncode(
                        account.getDeployDomain() +                                                          //微信开发者帐号对应的域名
                                wxConfig.get("wx.auth.url") +                                                        //微信授权后,跳转到本系统的地址
                                "?required=Y&redirect=" + getWechatUrl(gotoUrl).replace("?", "&"), "UTF-8"));          //最终跳转的地址

        return authorizeUrl;
    }

    /**
     * 根据请求参数(包含    code)
     * 得到认证授权的URL地址 openid
     *
     * @param parameterMap
     * @return
     */
    public synchronized String getWechatAuthUrl(Map<String, Object> parameterMap) {

        String code = parameterMap.get("code") + "";
        String redirect = parameterMap.get("redirect") + "";
        parameterMap.remove("redirect");

        //获取authtoken 对象信息
        String authorizeUrl = wxConfig.get("wx.oauth2.authopenid")
                .replace("APPID", this.account.getAppId())
                .replace("SECRET", this.account.getAppKey())
                .replace("CODE", code);

        AuthToken authToken = (AuthToken) PropertiesUtils.mapToObject(HttpClientUtil.sendGetToMap(authorizeUrl, null), AuthToken.class);

        //TODO 需要增加长期授权
//        authToken.get

        WechatUserInfo wechatUserInfo = getWechatUserInfo(authToken.getOpenid());

        parameterMap.put("nickname", StringUtils.getUrlDecode(wechatUserInfo.getNickname(), "utf-8"));
        parameterMap.put("sex", wechatUserInfo.getSex());
        parameterMap.put("headimgurl", wechatUserInfo.getHeadimgurl());
        parameterMap.put("openid", authToken.getOpenid());
        parameterMap.put("subscribe", wechatUserInfo.getSubscribe());
        parameterMap.put("city", StringUtils.getUrlDecode(wechatUserInfo.getCity(), "utf-8"));
        parameterMap.put("country", StringUtils.getUrlDecode(wechatUserInfo.getCountry(), "utf-8"));
        parameterMap.put("province", StringUtils.getUrlDecode(wechatUserInfo.getProvince(), "utf-8"));
//        authUserInfo.loadData(parameterMap);
//
//        logger.info("   >>>   wechatUserInfo:"+JsonUtil.ObjectToJSON(wechatUserInfo));

        return redirect + "?" + HttpRequestUtil.getSynchronizedParameter(parameterMap);
    }

    /**
     * 根据openId得到用户信息
     *
     * @param openId
     * @return
     */
    public WechatUserInfo getWechatUserInfo(String openId) {
        //根据openID获取用户的基本信息
        String userinfoUrl = wxConfig.get("wx.oauth2.userinfo")
                .replace("OPENID", openId)
                .replace("ACCESS_TOKEN", this.accessToken.getAccessToken());

        //获取微信用户信息
        return (WechatUserInfo) PropertiesUtils.mapToObject(HttpClientUtil.sendGetToMap(userinfoUrl, null), WechatUserInfo.class);
    }

    /**
     * 获取授权js SDK的认证信息
     *
     * @param url 需要授权的url页面
     * @return 认证json对象
     */
    public Map<String, String> grantAuth2JsSdk(String url) throws UnsupportedEncodingException, NoSuchAlgorithmException {
        Map<String, String> map = new HashMap<>();

        map.put("url", url);
        map.put("noncestr", Signature.randomString());
        map.put("timestamp", System.currentTimeMillis() / 1000 + "");
        map.put("jsapi_ticket", this.getJsapiTicket());                     //获取JsapiTicket

        //根据票据生成签名认证
        map.put("signature", Signature.signatureSHA1(map, false).toLowerCase());
        map.put("appId", this.account.getAppId());

        return map;
    }


    /**
     * 构造微信命令行控制类
     *
     * @param response
     * @return
     */
    public CommandController getCommandController(HttpServletRequest request, HttpServletResponse response) {
        CommandController commandController = new CommandController(this, request, response);

        return commandController;
    }

    /**
     * 微信开发者验证
     *
     * @return
     */
    private boolean wxValid(Map content) {
        String signature = content.get("signature") + "";
        String timestamp = content.get("timestamp") + "";
        String nonce = content.get("nonce") + "";

        return SignUtil.checkSignature(this.account.getToken(), signature, timestamp, nonce);
    }

    /**
     * 得到JsapiTicket票据信息
     *
     * @return
     */
    private String getJsapiTicket() {

        //判断是否有JsapiTicket和是否过期
        if (this.jsApiTicket == null || this.jsApiTicket.isValid()) {
            String accessToken = this.accessToken.getAccessToken();
            String sendUrl = wxConfig.get("wx.oauth2.jsToken")
                    .replace("ACCESS_TOKEN", accessToken);

            Map<String, Object> resultMap = HttpClientUtil.sendGetToMap(sendUrl, null);

            this.jsApiTicket = (JsApiTicket) PropertiesUtils.mapToObject(resultMap, JsApiTicket.class);

//            logger.info("ticket:"+this.jsApiTicket);
        }

        return this.jsApiTicket.getTicket();
    }

    /**
     * 得到微信的配置信息
     *
     * @return
     */
    @Override
    public Map<String, String> getWxConfig() {
        return wxConfig;
    }

    /**
     * 得到消息工具类
     *
     * @return
     */
    @Override
    public MsgUtils getMsgUtils() {
        return msgUtils;
    }

    /**
     * 得到支付工具类
     *
     * @return
     */
    @Override
    public PayUtils getPayUtils() {
        return payUtils;
    }

    /**
     * 得到当前微信账户信息
     *
     * @return
     */
    @Override
    public Account getAccount() {
        return account;
    }

    /**
     * 得到认证对象
     *
     * @return
     */
    @Override
    public AccessToken getAccessToken() {
        return accessToken;
    }

    /**
     * 得到菜单工具类
     *
     * @return
     */
    @Override
    public MenuUtils getMenuUtils() {
        return menuUtils;
    }

    @Override
    public QRCodeUtils getQRCodeUtils() {
        return this.qrCodeUtils;
    }

    /**
     * 处理微信事件
     *
     * @param request
     * @param response
     * @param wechatHandler
     */
    public void doWechatEvent(HttpServletRequest request, HttpServletResponse response, WechatHandler wechatHandler) {

        //获取XML请求内容并转换为MAP对象
        Map<String, Object> requestMap = XMLUtils.parseXml(request);
        String MsgType = requestMap.get("MsgType") + "";              //消息类型，event
        String Event = requestMap.get("Event") + "";                  //事件类型，subscribe(订阅)、unsubscribe(取消订阅)
        String Content = requestMap.get("Content") + "";
        String openId = requestMap.get("FromUserName") + "";
        String eventKey = requestMap.get("EventKey") + "";


        //通过数据库查询的方式得到关注的内容推送
        final Map<String, Object> reqMap = HttpRequestUtil.getParameterMap(request);
        logger.info("reqMap:" + JsonUtil.ObjectToJSON(reqMap));
//        String appName = reqMap.get("appType")+"";


        //关注事件
        if ("subscribe".equals(Event)) {
            logger.info("requestMap:" + JsonUtil.ObjectToJSON(requestMap));
            String xml = wechatHandler.subscribe(openId, requestMap);
            HttpResponseUtils.writerXml(response, xml);
        }
        //取消关注事件
        else if ("unsubscribe".equals(Event)) {
            String xml = wechatHandler.unsubscribe(openId, requestMap);
            HttpResponseUtils.writerXml(response, xml);
        } else if ("click".equals(Event.toLowerCase())) {
            CommandController commandController = this.getCommandController(request, response);
            wechatHandler.addListenerCommand(commandController);
            commandController.routeCondition(eventKey, openId);
        }

        //文本响应
        if ("text".equals(MsgType)) {
            CommandController commandController = this.getCommandController(request, response);
            wechatHandler.addListenerCommand(commandController);
            commandController.routeCondition(Content, openId);
        }
    }

    /**
     * 根据配型文件的键得到值
     *
     * @return
     */
    public String getConfigMap(String key) {
        return this.configMap.get(key) + "";
    }


    public static void main(String[] args) throws UnsupportedEncodingException {
//        HttpClientUtil.sendPostToString("http://localhost:8081/api/wx/validDeveloperWechat?appType=serviceadd","<xml><ToUserName><![CDATA[sdfsd]]></ToUserName><FromUserName><![CDATA[sdffgdfgdf]]></FromUserName><CreateTime>1486354324</CreateTime><MsgType>event</MsgType><Event>subscribe</Event></xml>");
//        System.out.println(new WechatUtils("wx-testusercar.properties").builderAuthUrl("http://localhost:8080/coccoool.move?appType="));

    }
}